package com.goldgov.code.template.commons.impl.tree;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.goldgov.code.meta.FieldInfo;
import com.goldgov.code.meta.ModuleInfo;
import com.goldgov.code.template.Layout;
import com.goldgov.code.template.ParamKeyName;
import com.goldgov.code.template.commons.CommonPlugin;
import com.goldgov.code.template.impl.DaoLayout;
import com.goldgov.code.template.impl.ServiceLayout;
import com.goldgov.code.template.impl.WebLayout;
import com.goldgov.code.template.point.CodePoint;
import com.goldgov.code.template.point.PointMapping;
import com.goldgov.code.template.point.impl.ImportCodePoint;
import com.goldgov.code.template.point.impl.InterfaceMethodCodePoint;
import com.goldgov.code.template.point.impl.MethodBodyCodePoint;
import com.goldgov.code.template.point.impl.MethodCodePoint;
import com.goldgov.code.template.point.impl.MyBatisXmlCodePoint;
import com.goldgov.code.utils.CodeUtils;
import com.goldgov.code.utils.FreemarkerUtils;
import com.goldgov.code.utils.StringUtils;

import freemarker.template.Template;

public class TreePlugin implements CommonPlugin{

	public static final String FIELD_NAME_TREE_PATH = "TREE_PATH";
	public static final String FIELD_NAME_PARENT_ID = "PARENT_ID";
	public static final String FIELD_NAME_TREE_VALUE = "TREE_VALUE";
	
	@Override
	public String name() {
		return "tree";
	}

	@Override
	public PointMapping doLayout(ModuleInfo module, Layout[] layouts,Map<String,Object> paramMap) {
		PointMapping pointMapping = new PointMapping();
		
		Map<String,Object> extParam = (Map<String, Object>) paramMap.get("extParam");
		boolean genDefField = Boolean.valueOf(""+extParam.get(ParamKeyName.GEN_DEFAULT_FIELD));
		
		String stateCondition = "";
		if(genDefField) {
			stateCondition = " AND STATE=1\r\n";
		}
		
		for (Layout layout : layouts) {
			if(layout instanceof DaoLayout) {
				List<CodePoint> codePointList = pointMapping.codePoint("*/*Dao.java.ftl");
				List<CodePoint> xmlPointList = pointMapping.codePoint("*/*Dao.xml.ftl");
				// Java Code
				String getAllMethod = "getAll" + module.getName() + "ByTree";
				String getMethod = "get" + module.getName() + "ByTree";
				
				String getMaxNodeValueMethod = "getMaxNodeValue";
				String getParentNodePathMethod = "getParentNodePath";
				codePointList.add(new InterfaceMethodCodePoint(getAllMethod,"List<" + module.getName() + ">","String treePath"));
				codePointList.add(new InterfaceMethodCodePoint(getMethod,"List<" + module.getName() + ">","String id"));
				
				codePointList.add(new InterfaceMethodCodePoint(getMaxNodeValueMethod,"Integer",""));
				codePointList.add(new InterfaceMethodCodePoint(getParentNodePathMethod,"String","@Param(\"parentID\") String parentID"));
				
				// MyBatis XML
				StringBuilder xml = new StringBuilder();
				xml.append(CodeUtils.code(1,"<select id=\"" + getAllMethod +"\">"));
				xml.append(CodeUtils.code(2, "<bind name=\"treePathLike\" value=\"${treePath}+'%'\"/>"));
				xml.append(CodeUtils.code(2, "SELECT <include refid=\"queryFields\"/> FROM " + module.getTableName() + " WHERE  "  + FIELD_NAME_TREE_PATH + " LIKE #{treePathLike} " + stateCondition +" ORDER BY " + FIELD_NAME_TREE_PATH));
				xml.append(CodeUtils.code(1, "</select>\r\n"));
				xml.append(CodeUtils.code(1, "<select id=\"" + getMethod +"\">"));
				String subQuery = "(SELECT " + FIELD_NAME_TREE_PATH + " FROM " + module.getTableName() + " WHERE " + module.getPkField().getName() + " = #{id})";
				xml.append(CodeUtils.code(2 ,"SELECT <include refid=\"queryFields\"/> FROM " + module.getTableName() + " WHERE  "  + FIELD_NAME_TREE_PATH + " = " + subQuery) + stateCondition);
				xml.append(CodeUtils.code(1 ,"</select>"));
				
				xml.append(CodeUtils.code(1 ,"<select id=\"" + getMaxNodeValueMethod +"\" resultType=\"Integer\">"));
				xml.append(CodeUtils.code(2 ,"SELECT max(TREE_VALUE) FROM " + module.getTableName()));
				xml.append(CodeUtils.code(1 ,"</select>"));
				
				xml.append(CodeUtils.code(1 ,"<select id=\"" + getParentNodePathMethod +"\" resultType=\"String\">"));
				xml.append(CodeUtils.code(2 ,"SELECT TREE_PATH FROM " + module.getTableName() + " WHERE PARENT_ID = #{parentID}"));
				xml.append(CodeUtils.code(1 ,"</select>"));
				
				xmlPointList.add(new MyBatisXmlCodePoint(xml.toString()));
			}else if(layout instanceof ServiceLayout) {
				List<CodePoint> interfacePointList = pointMapping.codePoint("*/*Service.java.ftl");
				List<CodePoint> implPoint = pointMapping.codePoint("**/*ServiceImpl.java.ftl");
				// Service Interface
				String getAllMethod = "getAll" + module.getName() + "ByTree";
				String getMethod = "get" + module.getName() + "ByTree";
				interfacePointList.add(new InterfaceMethodCodePoint(getAllMethod,"List<" + module.getName() + ">","String id"));
				interfacePointList.add(new InterfaceMethodCodePoint(getMethod,"List<" + module.getName() + ">","String id"));
				
				// Service Implement
				String allGetImplMethod = "List<" + module.getName() + "> " + getAllMethod + " (String id)";
				String getImplMethod = "List<" + module.getName() + "> " + getMethod + " (String id)";
				
				StringBuilder allGetImpl = new StringBuilder();
				StringBuilder getImpl = new StringBuilder();
				if(hasLayout(layouts,DaoLayout.class)) {
					String varName = StringUtils.lowerFirstChar(module.getName());
					allGetImpl.append(CodeUtils.varNewCustom(2, module.getName(),varName, "this.get" + module.getName() + "(id)"));
					allGetImpl.append(CodeUtils.varNewCustom(2, "String","treePath", varName + ".getTreePath()"));
					allGetImpl.append(CodeUtils.callMethod(2, "return " +StringUtils.lowerFirstChar(module.getName()) + "Dao", getAllMethod , "treePath"));
					
					getImpl.append(CodeUtils.callMethod(2, "return " + StringUtils.lowerFirstChar(module.getName()) + "Dao", getMethod , "id"));
				}else {
					allGetImpl.append(CodeUtils.code(2, "return null;"));
					getImpl.append(CodeUtils.code(2, "return null;"));
				}
				
				implPoint.add(new MethodCodePoint(allGetImplMethod,allGetImpl.toString()));
				implPoint.add(new MethodCodePoint(getImplMethod,getImpl.toString()));
				implPoint.add(new ImportCodePoint("com.goldgov.kcloud.core.utils.PathUtils"));
				
				Template template = FreemarkerUtils.getTemplate("tree/addTreeNode.ftl");
				StringWriter stringWriter = new StringWriter();
				Map<String,Object> dataMap = new HashMap<>();
				dataMap.put("module", module);
				try {
					template.process(dataMap, stringWriter);
					implPoint.add(new MethodBodyCodePoint("PRE_ADD",stringWriter.toString()));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				
			}else if(layout instanceof WebLayout) {
				List<CodePoint> webPointList = pointMapping.codePoint("*/*Controller.java.ftl");
				
				webPointList.add(new ImportCodePoint(LinkedHashMap.class));
				webPointList.add(new ImportCodePoint(ArrayList.class));
				
				String varName = StringUtils.lowerFirstChar(module.getName());
				StringBuilder codeBody = new StringBuilder();
				codeBody.append(CodeUtils.varNewCustom(2, "List<" + module.getName() + ">", varName+"List", varName+"Service.getAll" + module.getName() + "ByTree(" + varName + "ID)"));
				codeBody.append(CodeUtils.code(2, "List<Node<" + module.getName() + ">> nodeList = formatTreeNode(" + varName + "List);"));
				codeBody.append(CodeUtils.code(2,"return new JsonSuccessObject<>(nodeList);"));
				MethodCodePoint methodCodePoint = new MethodCodePoint("private","JsonObject<List<Node<" + module.getName() + ">>> list" + module.getName() + "Tree(String " + varName + "ID)",codeBody.toString());
				methodCodePoint.setAnnotation("@GetMapping(\"/tree/{" + varName + "ID}\")");
				webPointList.add(methodCodePoint);
				
				Template template = FreemarkerUtils.getTemplate("tree/tree.ftl");
				StringWriter stringWriter = new StringWriter();
				Map<String,Object> dataMap = new HashMap<>();
				dataMap.put("module", module);
				dataMap.put("nodeAttr", "name");//FIXME 定义树节点属性名
				try {
					template.process(dataMap, stringWriter);
					webPointList.add(new CodePoint("JAVA:METHOD") {
						@Override
						public String getCodes() {
							return stringWriter.toString();
						}});
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return pointMapping;
	}
	
	private boolean hasLayout(Layout[] layouts,Class<?> layoutClass) {
		for (Layout layout : layouts) {
			if(layout.getClass() == layoutClass) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean supports(ModuleInfo module, Layout[] layouts) {
		FieldInfo[] fields = module.getFields();
		boolean hasParentField = false;
		boolean hasTreePathField = false;
		boolean hasTreeValueField = false;
		for (FieldInfo fieldInfo : fields) {
			if(fieldInfo.getField().equals(FIELD_NAME_PARENT_ID)) {
				hasParentField = true;
			}else if(fieldInfo.getField().equals(FIELD_NAME_TREE_PATH)){
				hasTreePathField = true;
			}else if(fieldInfo.getField().equals(FIELD_NAME_TREE_VALUE)) {
				hasTreeValueField = true;
			}
		}
		return hasParentField && hasTreePathField && hasTreeValueField;
	}

}
